package com.qupai.lib_base.utils

import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.os.Looper
import android.util.Log
import android.widget.Toast
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ShellUtils
import com.blankj.utilcode.util.Utils
import com.drake.channel.receiveEventHandler
import com.drake.channel.sendEvent
import kotlinx.coroutines.CoroutineScope
import java.io.*
import java.text.SimpleDateFormat
import java.util.*
import kotlin.system.exitProcess

/**
 * 全局异常捕获
 * Created by ljw on 2017/7/3.
 */
class AppCrashHandler private constructor() : Thread.UncaughtExceptionHandler {
    private var mDebug: Boolean=false

    /**
     * 系统默认UncaughtExceptionHandler
     */
    private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null

    /**
     * context
     */
    private var mContext: Context? = null

    /**
     * 存储异常和参数信息
     */
    private val paramsMap: MutableMap<String, String> = HashMap()

    /**
     * 格式化时间
     */
    @SuppressLint("SimpleDateFormat")
    private val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss")
    private val TAG = this.javaClass.simpleName
    fun init(context: Context?,debug:Boolean) {
        mContext = context
        mDebug = debug
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        //设置该CrashHandler为系统默认的
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    /**
     * uncaughtException 回调函数
     */
    override fun uncaughtException(thread: Thread, ex: Throwable) {
        if (!handleException(ex) && mDefaultHandler != null) { //如果自己没处理交给系统处理
            mDefaultHandler!!.uncaughtException(thread, ex)
        } else { //自己处理
            try { //延迟3秒杀进程
                Thread.sleep(10000)
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            exitProcess(0)
        }
    }


    /**
     * 收集错误信息.发送到服务器
     *
     * @return 处理了该异常返回true, 否则false
     */
    private fun handleException(ex: Throwable?): Boolean {
        if (ex == null) {
            return false
        }
        sendEvent(ex, EVENT_APP_CRASH)
        //收集设备参数信息
        collectDeviceInfo(mContext)
        //添加自定义信息
        addCustomInfo()
        //使用Toast来显示异常信息
        object : Thread() {
            override fun run() {
                Looper.prepare()
                Toast.makeText(mContext, "程序异常，机器即将重启，请联系客服处理未完成订单", Toast.LENGTH_LONG).show()
                Looper.loop()
            }
        }.start()
        //保存日志文件
        try {
            saveCrashInfo2File(ex)
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
        return true
    }

    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    fun collectDeviceInfo(ctx: Context?) {
        //获取versionName,versionCode
        try {
            val pm = ctx!!.packageManager
            val pi = pm.getPackageInfo(ctx.packageName, PackageManager.GET_ACTIVITIES)
            if (pi != null) {
                val versionName = if (pi.versionName == null) "null" else pi.versionName
                val versionCode = pi.versionCode.toString() + ""
                paramsMap["versionName"] = versionName
                paramsMap["versionCode"] = versionCode
            }
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        }
        //获取所有系统信息
        val fields = Build::class.java.declaredFields
        for (field in fields) {
            try {
                field.isAccessible = true
                paramsMap[field.name] = field[null].toString()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }


    /**
     * 添加自定义参数
     */
    private fun addCustomInfo() {}
    private var fileName: String? = null

    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称, 便于将文件传送到服务器
     */
    @Throws(InterruptedException::class)
    private fun saveCrashInfo2File(ex: Throwable): String? {
        val sb = StringBuffer()
        for ((key, value) in paramsMap) {
            sb.append("$key=$value\n")
        }
        val writer: Writer = StringWriter()
        val printWriter = PrintWriter(writer)
        ex.printStackTrace(printWriter)
        var cause = ex.cause
        while (cause != null) {
            cause.printStackTrace(printWriter)
            cause = cause.cause
        }
        printWriter.close()
        val result = writer.toString()
        sb.append(result)
        try {
            val timestamp = System.currentTimeMillis()
            val time = format.format(Date())
            fileName = "crash-$time-$timestamp.log"
            if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
                val path = Environment.getExternalStorageDirectory().toString() + "/snapshotCrash/log/"
                val dir = File(path)
                if (!dir.exists()) {
                    dir.mkdirs()
                }
                val fos = FileOutputStream(path + fileName)
                fos.write(sb.toString().toByteArray())
                fos.close()
            }
            return fileName
        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            LogUtils.e("发生未捕获异常：DEBUG - $mDebug",sb)
            val param1 = Environment.getExternalStorageDirectory().toString() + "/snapshotCrash/log/" + fileName
            Log.i(TAG, "saveCrashInfo2File: " + FileUtils.isFileExists(param1))
            //            UploadLogFileService.startActionFoo(PaintingApplication.paintingApplication, param1);
            Thread.sleep(5000)
            if(mDebug){
                LogUtils.e("Debug环境，不重启系统")
            }else{
                LogUtils.e("Release环境，重启系统")
                reboot()
            }
//            myService.rebootSystem()
//            AppUtils.relaunchApp(true)
        }
        return null
    }

    companion object {

        const val EVENT_APP_CRASH = "APP_CRASH"


        @SuppressLint("StaticFieldLeak")
        private var mInstance: AppCrashHandler? = null

        /**
         * 获取CrashHandler实例
         */
        @get:Synchronized
        val instance: AppCrashHandler?
            get() {
                if (null == mInstance) {
                    mInstance = AppCrashHandler()
                }
                return mInstance
            }

        fun reboot() {
            ShellUtils.execCmd("reboot", true)
            val intent = Intent(Intent.ACTION_REBOOT)
            intent.putExtra("nowait", 1)
            intent.putExtra("interval", 1)
            intent.putExtra("window", 0)
            Utils.getApp().sendBroadcast(intent)
        }

        fun receiveAppCrashEvent(function2: (Throwable) -> Unit) {
            receiveEventHandler<Throwable>(EVENT_APP_CRASH) {
                function2.invoke(it)
            }
        }
    }
}